// Copyright Epic Games, Inc. All Rights Reserved.

#include "FFmpeg.h"
#include "Core.h"
#include "Modules/ModuleManager.h"
#include "Interfaces/IPluginManager.h"

extern  "C" {
#include "libavformat/avformat.h"

#if PLATFORM_ANDROID
#include "libavcodec/jni.h"
#endif

}

DEFINE_LOG_CATEGORY(LogFFMPEGLib);

#define LOCTEXT_NAMESPACE "FFFmpegModule"
FFFmpegModule::FFFmpegModule() {
    AVDeviceLibrary = nullptr;
    AVFilterLibrary = nullptr;
    PostProcLibrary = nullptr;
    SWScaleLibrary = nullptr;
    AVFormatLibrary = nullptr;
    AVCodecLibrary = nullptr;
    SWResampleLibrary = nullptr;
    AVUtilLibrary = nullptr;
}

void FFFmpegModule::log_callback(void*, int level, const char* format, va_list arglist) {

    char buffer[2048];
#if PLATFORM_WINDOWS
    vsprintf_s(buffer, 2048, format, arglist);
#else
    vsnprintf(buffer, 2048, format, arglist);
#endif
    FString str = TEXT("FFMPEG - ");
    str += buffer;

    switch (level) {
    case AV_LOG_TRACE:
        UE_LOG(LogFFMPEGLib, VeryVerbose, TEXT("%s"), *str);
        break;
    case AV_LOG_DEBUG:
        UE_LOG(LogFFMPEGLib, VeryVerbose, TEXT("%s"), *str);
        break;
    case AV_LOG_VERBOSE:
        UE_LOG(LogFFMPEGLib, Verbose, TEXT("%s"), *str);
        break;
    case AV_LOG_INFO:
        UE_LOG(LogFFMPEGLib, Display, TEXT("%s"), *str);
        break;
    case AV_LOG_WARNING:
        UE_LOG(LogFFMPEGLib, Warning, TEXT("%s"), *str);
        break;
    case AV_LOG_ERROR:
        UE_LOG(LogFFMPEGLib, Error, TEXT("%s"), *str);
        break;
    case AV_LOG_FATAL:
        UE_LOG(LogFFMPEGLib, Fatal, TEXT("%s"), *str);
        break;
    default:
        UE_LOG(LogFFMPEGLib, Display, TEXT("%s"), *str);
    }

}
void FFFmpegModule::StartupModule()
{
#if PLATFORM_ANDROID
    UE_LOG(LogFFMPEGLib, Verbose, TEXT("Avoid load the libraries once again on android"));
#else
    //AVUtilLibrary = LoadLibrary(TEXT("avutil"), TEXT("56"));
    //SWResampleLibrary = LoadLibrary(TEXT("swresample"), TEXT("3"));
    //AVCodecLibrary = LoadLibrary(TEXT("avcodec"), TEXT("58"));
    //AVFormatLibrary = LoadLibrary(TEXT("avformat"), TEXT("58"));
    //SWScaleLibrary = LoadLibrary(TEXT("swscale"), TEXT("5"));
    //PostProcLibrary = LoadLibrary(TEXT("postproc"), TEXT("55"));
    //AVFilterLibrary = LoadLibrary(TEXT("avfilter"), TEXT("7"));
    //AVDeviceLibrary = LoadLibrary(TEXT("avdevice"), TEXT("58"));
#endif

#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
    av_register_all();
#endif

    avformat_network_init();
    av_log_set_level(AV_LOG_INFO);

    av_log_set_callback(&log_callback);

    UE_LOG(LogFFMPEGLib, Display, TEXT("FFmpeg AVCodec version: %d.%d.%d"), LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO);
    UE_LOG(LogFFMPEGLib, Display, TEXT("FFmpeg license: %s"), UTF8_TO_TCHAR(avformat_license()));

	//if (AVDeviceLibrary)
	//{
	//	// Call the test function in the third party library that opens a message box
	//	//ExampleLibraryFunction();
	//}
	//else
	//{
	//	FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("ThirdPartyLibraryError", "Failed to load ffmpeg third party library"));
	//}
#if PLATFORM_ANDROID
    // hopefully this is early enough; we don't have a way add into JNI_OnLoad in AndroidJNI.cpp

    if (GJavaVM == NULL) {
        UE_LOG(LogFFMPEGLib, Error, TEXT("The global vm hasn't been initialized"));
    }
    else {
        UE_LOG(LogFFMPEGLib, Display, TEXT("Initialize virtual machine for FFMPEG"));
        av_jni_set_java_vm(GJavaVM, NULL);
    }
#endif
}
void* FFFmpegModule::LoadLibrary(const  FString& name, const FString& version) {

    FString BaseDir = IPluginManager::Get().FindPlugin("FFmpeg")->GetBaseDir();

    FString LibDir;
    FString extension;
    FString prefix;
    FString separator;
#if PLATFORM_MAC
    LibDir = FPaths::Combine(*BaseDir, TEXT("ThirdParty/ffmpeg/lib/osx"));
    extension = TEXT(".dylib");
    prefix = "lib";
    separator = ".";
#elif PLATFORM_WINDOWS
    extension = TEXT(".dll");
    prefix = "";
    separator = "-";
#if PLATFORM_64BITS
    LibDir = FPaths::Combine(*BaseDir, TEXT("Binaries/Win64"));
    UE_LOG(LogFFMPEGLib, Display, TEXT("%s"),*LibDir );
#else
    LibDir = FPaths::Combine(*BaseDir, TEXT("ThirdParty/ffmpeg/bin/vs/win32"));
#endif
#endif
    if (!LibDir.IsEmpty()) {
        FString LibraryPath = FPaths::Combine(*LibDir, prefix + name + separator + version + extension);
        return FPlatformProcess::GetDllHandle(*LibraryPath);
    }
    return nullptr;
}

void FFFmpegModule::ShutdownModule()
{
	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
	// we call this function before unloading the module.

	// Free the dll handle
    if (AVDeviceLibrary) FPlatformProcess::FreeDllHandle(AVDeviceLibrary);
    if (AVFilterLibrary) FPlatformProcess::FreeDllHandle(AVFilterLibrary);
    if (PostProcLibrary) FPlatformProcess::FreeDllHandle(PostProcLibrary);
    if (SWScaleLibrary) FPlatformProcess::FreeDllHandle(SWScaleLibrary);
    if (AVFormatLibrary) FPlatformProcess::FreeDllHandle(AVFormatLibrary);
    if (AVCodecLibrary) FPlatformProcess::FreeDllHandle(AVCodecLibrary);
    if (SWResampleLibrary) FPlatformProcess::FreeDllHandle(SWResampleLibrary);
    if (AVUtilLibrary) FPlatformProcess::FreeDllHandle(AVUtilLibrary);
}

#undef LOCTEXT_NAMESPACE
	
IMPLEMENT_MODULE(FFFmpegModule, FFmpeg)
